package work.linruchang.util.bean.wrapper.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Filter;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.sql.SqlUtil;
import lombok.Data;
import work.linruchang.util.EnhanceClassUtil;
import work.linruchang.util.EnhanceReflectUtil;
import work.linruchang.util.EnhanceStrUtil;
import work.linruchang.util.bean.wrapper.db.SqlColumnInfo;
import work.linruchang.util.bean.wrapper.db.SqlFragment;
import work.linruchang.util.bean.wrapper.db.TableInfo;
import work.linruchang.util.bean.wrapper.BaseWrapper;
import work.linruchang.util.bean.wrapper.HavingWrapper;
import work.linruchang.util.bean.wrapper.WhereWrapper;

import java.io.Serializable;
import java.util.*;


/**
 *
 *
 * @author LinRuChang
 * @version 1.0
 * @date 2022/01/14
 * @since 1.8
 **/
@Data
public class BaseWrapperImpl<FirstTable, SecondTable> implements Serializable, BaseWrapper<FirstTable, SecondTable> {

    protected boolean isCountFlag = false;
    //是否需要逻辑删除支持
    protected boolean logicDeleteSupport = true;

    protected Class<FirstTable> firstTableClazz; //主表
    protected Class<SecondTable> secondTableClazz; //从表

    protected StringBuilder sqlSB = new StringBuilder(); //sql语句

    private List<SqlColumnInfo> whereColumnInfos = new ArrayList<>();
    protected List<String> whereSqlFragments = new ArrayList<>();  //where条件片段
    protected List<Object> whereSqlParams = new ArrayList<>();  //待填充的参数
    protected List<Class> whereSqlParamsTypes = new ArrayList<>();   //参数在数据库的类型

    private List<SqlColumnInfo> onColumnInfos = new ArrayList<>();
    protected List<String> joinSqlFragments = new ArrayList<>(); //连表sql片段
    protected List<Object> joinOnSqlParams = new ArrayList<>();  //on的参数
    protected List<Class> joinOnSqlParamsTypes = new ArrayList<>();   //on的参数在数据库的类型

    protected Set<String> groupByFieldSqlFragments = new LinkedHashSet<>();  //需要分组的字段
    protected List<String> havingWhereSqlFragments = new ArrayList<>(); //分组筛选的条件
    protected List<Object> havingWhereSqlParams = new ArrayList<>(); //分组筛选的条件参数值
    protected List<Class> havingWhereSqlParamsTypes = new ArrayList<>(); //分组筛选的条件参数在数据库的类型

    protected Set<String> orderBySqlFragments = new LinkedHashSet<>(); //排序的字段片段

    protected Long limitStartIndex = -1L; //从第N条记录开始查找
    protected Long limitPageSize = -1L;//每页显示的条数
    protected Long pageNum = -1L;
    protected Long pageSize = -1L;

    /**
     * 所有表信息
     */
    Map<Class, TableInfo> allTableMap = new HashMap<>();


    /**
     * 后置处理SQL
     */
    @Override
    public void postSQLDeal() {
        if(this instanceof WhereWrapper) {
            final WhereWrapper whereWrapper = (WhereWrapper) this;

            //逻辑删除处理
            if(logicDeleteSupport && !(whereWrapper instanceof HavingWrapper)) {

                //按表进行区分条件
                final List<List<SqlColumnInfo>> sqlColumnInfoGroup = CollUtil.groupByField(whereColumnInfos, "tableInfo");

                //填充缺失的表 == 假SqlColumnInfo条件
                CollUtil.forEach(allTableMap,new CollUtil.KVConsumer<Class, TableInfo>(){
                    @Override
                    public void accept(Class clazz, TableInfo tableInfo, int index) {
                        boolean findFlag = false;
                        for(List<SqlColumnInfo> sameTableSqlColumnInfos : sqlColumnInfoGroup) {
                            if(CollUtil.isNotEmpty(sameTableSqlColumnInfos)) {
                                if(sameTableSqlColumnInfos.get(0).getTableInfo() == tableInfo) {
                                    findFlag = true;
                                    break;
                                }
                            }
                        }
                        if(!findFlag) {
                            sqlColumnInfoGroup.add(Arrays.asList(new SqlColumnInfo(tableInfo,null,null,null,null,SqlColumnInfo.SQL_TYPE_WHERE)));
                        }
                    }
                });

                //填充逻辑删除条件
                if(CollUtil.isNotEmpty(sqlColumnInfoGroup)) {
                    CollUtil.forEach(sqlColumnInfoGroup.iterator(), new CollUtil.Consumer<List<SqlColumnInfo>>() {
                        @Override
                        public void accept(List<SqlColumnInfo> sqlColumnInfos, int index) {
                            TableInfo tableInfo = sqlColumnInfos.get(0).getTableInfo();
                            Class dbBeanClazz = tableInfo.getDbBeanClazz();

                            //检索是否有is_del的判断条件
                            SqlColumnInfo sqlColumnInfo = CollUtil.findOneByField(sqlColumnInfos, "columnName", "is_del");

                            //无is_del判断条件且此表支持逻辑删除则添加逻辑非删除条件
                            if((ReflectUtil.hasField(dbBeanClazz,"is_del") || ReflectUtil.hasField(dbBeanClazz,"isDel")) && sqlColumnInfo == null) {
                                whereWrapper.eq(dbBeanClazz, "is_del", 0);
                            }
                        }
                    });
                }
            }
        }


    }

    /**
     * 获取系统主表信息
     * @return
     */
    @Override
    public TableInfo getMasterTableInfo() {
        Map.Entry<Class, TableInfo> one = CollUtil.findOne(allTableMap.entrySet(), new Filter<Map.Entry<Class, TableInfo>>() {
            @Override
            public boolean accept(Map.Entry<Class, TableInfo> classTableInfoEntry) {
                return classTableInfoEntry.getValue().getMasterTableFlag();
            }
        });
        return one != null ? one.getValue() : null;
    }

    @Override
    public int joinSqlFragmentsIndexOf(String joinFragment, Class joinTable) {
        TableInfo secondTableInfo = getTableInfo(joinTable);
        final String joinSqlPrefix = StrUtil.format(" {} {} AS {} ", joinFragment, secondTableInfo.getTableName(), secondTableInfo.getTableAlias());
        int result = -1;
        for (String joinSqlFragment : joinSqlFragments) {
            ++result;
            if (StrUtil.containsAny(joinSqlFragment, joinSqlPrefix)) {
                break;
            }
        }
        return result;
    }

    /**
     * 增加连表 == 不会重复添加，都设置为非主表
     *
     * @param clazzs
     * @return
     */
    @Override
    public Map<Class, TableInfo> addTables(Class... clazzs) {
        if (ArrayUtil.isNotEmpty(clazzs)) {
            for (Class clazz : clazzs) {
                addTable(clazz, false);
            }
        }
        return allTableMap;
    }

    /**
     * 增加表 == 不会重复添加
     *
     * @param clazz 表
     * @param masterTableFlag 表{clazz}是否是主表
     * @return
     */
    @Override
    public Map<Class, TableInfo> addTable(Class clazz, boolean masterTableFlag) {
        Assert.notNull(clazz,"缺乏表反射，请检查");

        //主表校验
        TableInfo masterTableInfo = getMasterTableInfo();
        if(masterTableInfo != null && masterTableFlag ) {
            Assert.isTrue(isMasterTable(clazz),"错误：待添加主表【{}】，然而一个Wrapper仅能添加一个主表，当前Wrapper的主表是【{}】, 请检查", clazz.getSimpleName(), masterTableInfo.getDbBeanClazz().getName());
            setFirstTableClazz(clazz);
        }

        if (!allTableMap.containsKey(clazz)) { //不存在直接插入
            allTableMap.put(clazz, new TableInfo(clazz, masterTableFlag));
        }
        return allTableMap;
    }

    /**
     * 增加表 == 不会重复添加
     *
     * @param tableInfo 表信息
     * @return
     */
    @Override
    public Map<Class, TableInfo> addTable(TableInfo tableInfo) {
        if (!allTableMap.containsKey(tableInfo.getDbBeanClazz())) {
            allTableMap.put(tableInfo.getDbBeanClazz(), tableInfo);
        }
        return allTableMap;
    }

    /**
     * 验证{clazz}是否是主表
     *
     * @param clazz 表
     * @return
     */
    @Override
    public boolean isMasterTable(Class clazz) {
        return ObjectUtil.equal(clazz, firstTableClazz);
    }

    /**
     * 判断{tableAlias}是否是主表
     *
     * @param tableAlias 表别名 == 仅判断表别名不会用表主名去判断
     * @return
     */
    @Override
    public boolean isMasterTable(String tableAlias) {
        TableInfo masterTable = getTableInfo(firstTableClazz);
        return StrUtil.equals(masterTable.getTableAlias(), tableAlias);
    }

    /**
     * 验证{columnName}是否是主表的属性列
     * 即columnName是否在主表中存有
     *
     * @param columnName tableClazz {columnName}列所在的表
     * @param columnName 列名
     * @return
     */
    @Override
    public boolean isMasterTableColumn(Class tableClazz, String columnName) {
        String tableColumnName = EnhanceReflectUtil.getDBColumnName(tableClazz, columnName);
        String masterTableColumnName = EnhanceReflectUtil.getDBColumnName(getFirstTableClazz(), columnName);
        return StrUtil.equals(tableColumnName, masterTableColumnName);
    }

    /**
     * 验证{columnName}是否不是主表的属性列
     * 即columnName是否在主表中存有
     *
     * @param columnName tableClazz {columnName}列所在的表
     * @param columnName 列名
     * @return
     */
    @Override
    public boolean isNotMasterTableColumn(Class tableClazz, String columnName) {
        return !isMasterTableColumn(tableClazz, columnName);
    }


    /**
     * 获取此sql的所有参数值 == 即问号所表示的值
     *
     * @return
     */
    @Override
    public List<Object> getSqlParams() {
        List<Object> result = new ArrayList<>();
        CollUtil.addAll(result, joinOnSqlParams);
        CollUtil.addAll(result, whereSqlParams);
        CollUtil.addAll(result, havingWhereSqlParams);
        if (ObjectUtil.isAllNotEmpty(limitStartIndex, limitPageSize) && limitStartIndex >= 0 && limitPageSize > 0) {
            result.add(limitStartIndex);
            result.add(limitPageSize);
        }
        return result;
    }

    /**
     * 获取此sql的所有参数值 == 即问号所表示的值
     *
     * @return
     */
    @Override
    public List<Class> getSqlParamsTypes() {
        List<Class> result = new ArrayList<>();
        CollUtil.addAll(result, joinOnSqlParamsTypes);
        CollUtil.addAll(result, whereSqlParamsTypes);
        CollUtil.addAll(result, havingWhereSqlParamsTypes);

        if (ObjectUtil.isAllNotEmpty(limitStartIndex, limitPageSize) && limitStartIndex >= 0 && limitPageSize > 0) {
            result.add(Long.class);
            result.add(Long.class);
        }

        return result;
    }

    /**
     * 表信息获取
     *
     * @param clazz
     * @return
     */
    @Override
    public TableInfo getTableInfo(Class clazz) {
        TableInfo tableInfo = allTableMap.get(clazz);
        Assert.notNull(tableInfo, "当前Wrapper缺乏【{}】表信息TableInfo，请检查", clazz.getName());
        return tableInfo;
    }

    /**
     * 表信息获取通过表别名
     *
     * @param tableAlias 表别名
     * @return
     */
    @Override
    public TableInfo getTableInfo(final String tableAlias) {
        Collection<TableInfo> tableInfos = allTableMap.values();
        return CollUtil.findOne(tableInfos, new Filter<TableInfo>() {
            @Override
            public boolean accept(TableInfo tableInfo) {
                return StrUtil.equals(tableInfo.getTableAlias(), tableAlias);
            }
        });
    }


    /**
     * 表名获取
     * <p>
     * 获取失败，抛出异常
     *
     * @param clazz
     * @return
     */
    @Override
    public String getTableName(Class clazz) {
        return getTableInfo(clazz).getTableName();
    }

    /**
     * 表别名获取
     * <p>
     * 获取失败，抛出异常
     *
     * @param clazz
     * @return
     */
    @Override
    public String getTableAlias(Class clazz) {
        return getTableInfo(clazz).getTableAlias();
    }

    /**
     * 判断是否是列字段的SQL语句
     * @param columnName
     * @return
     */
    @Override
    public boolean isColumnNameFlag(String columnName) {
        columnName = StrUtil.trim(columnName);
        if (StrUtil.containsAnyIgnoreCase(columnName, SqlFragment.SELECT_COUNT, SqlFragment.ASTERISK)) {
            return false;
        }
        return true;
    }


    /**
     * 表列名添加表别名后的内容
     * 格式：name => cuom_fdf.name
     * 获取失败，抛出异常
     *
     * @param clazz      表
     * @param columnName 表字段名 == 会进行字段校验
     * @return
     */
    @Override
    public String getColumnAddTableAlias(Class clazz, String columnName) {
        TableInfo tableInfo = getTableInfo(clazz);
        String dbColumnName = columnName;
        if (!StrUtil.equalsAnyIgnoreCase(columnName, SqlFragment.ASTERISK, SqlFragment.SELECT_COUNT)) {
            dbColumnName = EnhanceReflectUtil.getDBColumnName(clazz, columnName);
            Assert.notBlank(dbColumnName, "列名出错：【{}】列不在表【{}】中存在，请检查", columnName, tableInfo.getTableName());
        }
        if (StrUtil.equalsAnyIgnoreCase(columnName, SqlFragment.SELECT_COUNT)) {
            return columnName;
        } else {
            return StrUtil.addPrefixIfNot(dbColumnName, tableInfo.getTableAlias() + StrUtil.DOT);
        }
    }

    /**
     * 校验{columnNames}是否在表{clazz}中存在
     *
     * @param clazz       表
     * @param columnNames 列名
     * @return
     */
    @Override
    public boolean checkColumnNames(Class clazz, String... columnNames) {
        ArrayUtil.isNotEmpty(columnNames);
        columnNames = ArrayUtil.removeBlank(columnNames);
        Assert.notEmpty(columnNames, "缺乏需要校验的字段，请检查");
        for (String columnName : columnNames) {
            if (StrUtil.equalsAnyIgnoreCase(columnName, SqlFragment.ASTERISK, SqlFragment.SELECT_COUNT)) {
                continue;
            }
            Assert.notBlank(EnhanceReflectUtil.getDBColumnName(clazz, columnName), "列名出错：{}列不在表{}中存在，请检查", columnName, getTableName(clazz));
        }
        return true;
    }

    /**
     * 获取SQL
     * 参数由问号代替
     * <p>
     * 完整的SQL，请查看{@link BaseWrapperImpl#getCompleteSQL()}
     *
     * @return
     */
    @Override
    public String getSQL() {
        throw new RuntimeException("不支持的操作，请检查");
    }

    /**
     * （美化）获取SQL
     * 参数由问号代替
     * <p>
     * 完整的SQL，请查看{@link BaseWrapperImpl#getCompleteSQL()}
     *
     * @return
     */
    @Override
    public String getFormatSQL() {
        return SqlUtil.formatSql(getSQL());
    }

    /**
     * 获取完整SQL语句（实际参数值取代问号）
     *
     * @return
     */
    @Override
    public String getCompleteSQL() {
        String sql = getSQL();
        sql = StrUtil.replace(sql, EnhanceStrUtil.QUESTION_MARK, "{}");

        //问号指代的所有参数值
        List<Object> sqlParamss = getSqlParams();
        //问号指代的所有参数的字段类型
        List<Class> sqlParamsTypes = getSqlParamsTypes();

        for (int i = 0; i < sqlParamss.size(); i++) {
            Class sqlParamsType = sqlParamsTypes.get(i);
            Object sqlParam = sqlParamss.get(i);

            if (sqlParam == null) {
                sql = StrUtil.format(sql, SqlFragment.WHERE_NULL);
            } else {
                if (EnhanceClassUtil.isNumberType(sqlParamsType)) {
                    sql = StrUtil.format(sql, sqlParam);
                } else if (ObjectUtil.equal(Date.class, sqlParamsType) && sqlParam != null) {
                    String dateFormat = DatePattern.NORM_DATETIME_FORMAT.format(sqlParam);
                    sql = StrUtil.format(sql, StrUtil.format(" '{}' ", dateFormat));
                } else {
                    sql = StrUtil.format(sql, StrUtil.format(" '{}' ", sqlParam));
                }
            }
        }

        return sql;
    }

    /**
     * （美化）获取完整SQL语句（实际参数值取代问号）
     *
     * @return
     */
    @Override
    public String getCompleteFormatSQL() {
        return SqlUtil.formatSql(getCompleteSQL());
    }


}
